local function copy_value(value)
    if type(value) ~= "table" then
        return value
    end
    local cpt = {}
    for k,v in pairs(value) do
        cpt[copy_value(k)] = copy_value(v)
    end

    return setmetatable(cpt,value)
end

local function merge(taba,tabb)
    if tabb == nil then
        return taba
    end
    for k,v in pairs(tabb) do
        if type(taba[k]) == "table" and type(v) == "table" then
            taba[k] = merge(taba[k],tabb[k])
        elseif taba[k] == nil then
            taba[copy_value(k)] = copy_value(v)
        end
    end
    return taba
end 

local function get_merges(m,merges)
    if #merges <= 0 then return m end
    for i,mer in ipairs(merges) do
        m[mer.__name__] = true;
        get_merges(m,mer)
    end
    return m
end

local function get_merges_class(m,merges)
    if #merges <= 0 then return m end
    for i,mer in ipairs(merges) do
        table.insert(m,mer)
        get_merges_class(m,mer.__merge_class__)
    end
    return m
end

local function new(self,...)
    local instance = {}
    self.__merge__(instance,self)
    if instance.__init__ then
        instance:__init__(...)
    end

    setmetatable(instance,instance)
    return instance
end

local function is(self,is_class_name)
    return self.__merges__[is_class_name] ~= nil
end

return function(name,...)
    local merges = {...}
    return function(class)
        class.__name__  = name
        class.__merge__ = merge
        class.__merges__ = get_merges({[class.__name__] = true},merges)
        class.__merge_class__ = get_merges_class({},merges)

        for k,cls in ipairs(merges) do
            merge(class.__merges__,cls.__merges__)
        end
        
        if #merges ~= 0 then
            for i,cls_or_comp in ipairs(merges) do
                class:__merge__(cls_or_comp)
            end
        end

        class.__new__ = new
        class.is = is
        class.__call = class.__new__

        return setmetatable(class,class) 
    end
end